#include "capture.h"
#include "sys/time.h"
#include "unistd.h"
#include "libmaix_err.h"

AWCapture::AWCapture() :IVidCapture(),
                        _callback(NULL),
                        _callback_ctx(NULL),
                        _isStarted(false),
                        _width(0),
                        _height(0)
{

}

AWCapture::~AWCapture()
{
}

int32_t AWCapture::Start( int32_t id, int32_t w, int32_t h ){

    int32_t ret = -1;
    if( _isStarted == true ){
        printf( "Start failed.......\r\n" );
        Stop();
    }

    _cam_inst = libmaix_cam_creat( w, h );
    if( _cam_inst == NULL ){
        printf( "create_camera failed\r\n" );
        return -1;
    }

    ret = _cam_inst->strat_capture(_cam_inst);
    if( ret != 0 ){
        printf( "strat_capture failed\r\n" );
        return -1;
    }

    memory_base_create_fq( w, h, w, h, VID_FRAME_TYPE_NV21, 5, &_vid_cap_fq );

    pthread_create(&_callback_thread, NULL, _callback_func, this);

    _width = w;
    _height = h;

    _isStarted = true;

    return ret;
}

int32_t AWCapture::Regcb( VidCaptureCallback captureCallback, void *ctx ){

    if( captureCallback == NULL ){
        printf( "captureCallback:%p\r\n", captureCallback );
        return -1;
    }
    _callback = captureCallback;
    _callback_ctx = ctx;
    return 0;
}

int32_t AWCapture::Stop(){

    if( _isStarted == false ){
        printf( "no Start......\r\n" );
        return -1;
    }

    _isStarted = false;

    pthread_join( _callback_thread, NULL );
    sleep(1);

    libmaix_cam_destroy(&_cam_inst);
    memory_base_destory_fq( _vid_cap_fq );

    return 0;
}

void *AWCapture::_callback_func(void * ctx){
    int32_t ret = 0;
    AWCapture *thiz = (AWCapture*)ctx;
    while(1){
        if( ret != 0 || thiz->_isStarted == false){
            printf( "_callback_func exit.....\r\n" );
            return NULL;
        }
        ret = thiz->do_callback();
    }
    return NULL;
}

int32_t AWCapture::do_callback(){

    int32_t ret = 0;
    struct timeval tv;
    struct MemoryFrame frame;

    ret = memory_base_fq_acquire( _vid_cap_fq, &frame );
    if( ret != 0 ){
        printf( "%s memory_base_fq_acquire failed :%d\r\n", __FUNCTION__, ret );
        return -1;
    }
    ret = _cam_inst->capture(_cam_inst, frame.addr );
    if(ret != LIBMAIX_ERR_NONE)
    {
        // not ready， sleep to release CPU
        if(ret == LIBMAIX_ERR_NOT_READY)
        {
            frame.ops.dec_ref(frame.frame);
            usleep(20*1000);
            return 0;
        }
        else
        {
            printf("capture fail: %d\n", ret);
            frame.ops.dec_ref(frame.frame);
            return -1;
        }
    }

    gettimeofday( &tv, NULL );
    frame.timestamp = tv.tv_sec*1000+tv.tv_usec/1000;
    _callback( &frame, _callback_ctx );
    frame.ops.dec_ref(frame.frame);

    return ret;
}




int32_t aw_vid_cap_init( int32_t width, int32_t height, /*out*/AW_VID_CAPTURE_HANDLE *handle ){
    vid_cap_ins *ins = new vid_cap_ins();
    ins->width = width;
    ins->height = height;
    ins->IVidCap = new AWCapture();
    if( ins->IVidCap == NULL ){
        return -1;
        printf( "get_instance failed\r\n" );
    }
    *handle = (AW_VID_CAPTURE_HANDLE)ins;
    return 0;
}


int32_t aw_vid_cap_deinit( /*in*/AW_VID_CAPTURE_HANDLE handle ){
    int32_t ret = 0;
    vid_cap_ins *ins = (vid_cap_ins*)handle;
    printf( " %s handle:%p, IVidCap:%p\r\n",__FUNCTION__ , handle, ins->IVidCap  );
    if( ins == NULL || ins->IVidCap == NULL ){
        printf( "%s param Error....\r\n", __FUNCTION__ );
        return -1;
    }
    delete ins->IVidCap;
    delete ins;
    return ret;
}

int32_t aw_vid_cap_start( /*in*/AW_VID_CAPTURE_HANDLE handle){

    int32_t ret = 0;
    vid_cap_ins *ins = (vid_cap_ins*)handle;
    if( ins == NULL || ins->IVidCap == NULL ){
        printf( "%s param Error....\r\n", __FUNCTION__ );
        return -1;
    }
    ret = ins->IVidCap->Start(0, ins->width, ins->height);
    if( ret < 0 ){
        printf( "aw_vid_cap_start failed..............\r\n" );
        return -1;
    }
    return ret;
}


int32_t aw_vid_cap_stop( /*in*/AW_VID_CAPTURE_HANDLE handle ){

    int32_t ret = 0;
    vid_cap_ins *ins = (vid_cap_ins*)handle;
    if( ins == NULL || ins->IVidCap == NULL ){
        printf( "%s param Error....\r\n", __FUNCTION__ );
        return -1;
    }
    ret = ins->IVidCap->Stop();
    if( ret < 0 ){
        printf( "aw_vid_cap_stop failed\r\n" );
        return -1;
    }
    return ret;
}

int32_t aw_vid_cap_regcb( /*in*/AW_VID_CAPTURE_HANDLE handle, VidCaptureCallback captureCallback, void *ctx ){

    int32_t ret = 0;
    vid_cap_ins *ins = (vid_cap_ins*)handle;
    printf( " %s handle:%p, IVidCap:%p\r\n",__FUNCTION__ , handle, ins->IVidCap  );
    if( ins == NULL || ins->IVidCap == NULL ){
        printf( "%s param Error....\r\n", __FUNCTION__ );
        return -1;
    }
    ret = ins->IVidCap->Regcb(captureCallback, ctx);
    if( ret < 0 ){
        printf( "aw_vid_cap_regcb failed\r\n" );
        return -1;
    }
    return ret;

}